﻿using Base;
using Microsoft.Win32;
using Models;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;

namespace ViewModels
{
    public class MainViewModel : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler? PropertyChanged;

        private SerialPort serialPort = new SerialPort();
        private LogProvider LogProvider;

        private bool _isStart;
        public bool IsStart
        {
            get { return _isStart; }
            set
            {
                _isStart = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsStart"));

                if (!value)
                    this.ShowMessage("监控未开始，请打开监控开关");
            }
        }

        private bool _isLog;

        public bool IsLog
        {
            get { return _isLog; }
            set
            {
                _isLog = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("IsLog"));
            }
        }

        private int _blurRadius = 0;

        public int BlurRadius
        {
            get { return _blurRadius; }
            set
            {
                _blurRadius = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("BlurRadius"));
            }
        }

        private DateTime _currentDate;

        public DateTime CurrentDate
        {
            get { return _currentDate; }
            set
            {
                _currentDate = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("CurrentDate"));
            }
        }

        private string _message;

        public string Message
        {
            get { return _message; }
            set
            {
                _message = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Message"));
            }
        }

        private string _msgColor = "#90EE90";

        public string MsgColor
        {
            get { return _msgColor; }
            set
            {
                _msgColor = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("MsgColor"));
            }
        }

        public List<string> PortNameList { get; set; }
        public string PortName { get; set; }

        public int BaudRate { get; set; } = 9600;
        public List<int> BaudRateList { get; set; } =
            new List<int> { 4800, 9600, 14400, 19200, 38400, 56000, 57600, 115200, 128000, 256000 };

        private string _logPath;

        public string LogPath
        {
            get { return _logPath; }
            set
            {
                _logPath = value;
                PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("LogPath"));
            }
        }

        public Command ConfigCommand { get; set; }
        public Command ConfigCloseCommand { get; set; }
        public Command LogPathSelectCommand { get; set; }
        public Command OpenLogPathCommand { get; set; }



        public List<ItemModel> ItemList { get; set; }

        private List<Task> tasks = new List<Task>();

        private CancellationTokenSource cts = new CancellationTokenSource();

        public MainViewModel()
        {
            // 配置数据的初始化
            try
            {
                if (File.Exists("config.json"))
                {
                    string json = File.ReadAllText("config.json");
                    Config config = System.Text.Json.JsonSerializer.Deserialize<Config>(json);
                    this.PortName = config.PortName;
                    this.BaudRate = config.BaudRate;
                    this.LogPath = config.LogPath;
                }
                else
                {
                    this.ShowMessage("请配置参数", false);
                }

            }
            catch (Exception ex)
            {
                this.ShowMessage("初始化参数加载失败，" + ex.Message, false);
                return;
            }

            LogProvider = new LogProvider(this.LogPath);

            ItemList = new List<ItemModel>();
            ItemList.Add(new ItemModel() { SlaveId = 1 });
            ItemList.Add(new ItemModel() { SlaveId = 2 });
            ItemList.Add(new ItemModel() { SlaveId = 3 });
            ItemList.Add(new ItemModel() { SlaveId = 4 });

            var task = Task.Run(async () =>
            {
                while (!cts.IsCancellationRequested)
                {
                    CurrentDate = DateTime.Now;
                    await Task.Delay(new TimeSpan(1, 0, 0, 0, 0));
                }
            }, cts.Token);
            tasks.Add(task);

            PortNameList = SerialPort.GetPortNames().ToList();

            ConfigCommand = new Command(Config);
            ConfigCloseCommand = new Command(ConfigClose);
            LogPathSelectCommand = new Command(LogPathSelect);
            OpenLogPathCommand = new Command(OpenLogFolder);

            this.StartMonitor();
        }

        private void Config()
        {
            IsStart = false;
            BlurRadius = 10;
        }

        private void ConfigClose()
        {
            BlurRadius = 0;

            // 数据保存动作
            try
            {
                Config config = new Config();
                config.PortName = this.PortName;
                config.BaudRate = this.BaudRate;
                config.LogPath = this.LogPath;

                var json = System.Text.Json.JsonSerializer.Serialize(config);
                File.WriteAllText("config.json", json);

                this.Message = "";
                this.MsgColor = "#90EE90";

                LogProvider.WriteLog("配置文件修改完成", this.IsLog);
            }
            catch (Exception ex)
            {
                LogProvider.WriteLog("写配置文件失败！" + ex.Message, this.IsLog, isError: true);
                this.ShowMessage("写配置文件失败！" + ex.Message, false);
            }

        }

        private void LogPathSelect()
        {
            var folderDialog = new OpenFolderDialog
            {
                Title = "请选择文件夹",// 对话框标题
                InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86)// 初始化目录
            };
            if (folderDialog.ShowDialog() == true)
            {
                this.LogPath = folderDialog.FolderName;

                LogProvider.LogPath = this.LogPath;
            }
        }

        private void OpenLogFolder()
        {
            if (Directory.Exists(this.LogPath))
            {
                Process.Start("explorer.exe", this.LogPath);
            }
        }

        private void StartMonitor()
        {
            var task = Task.Factory.StartNew(async () =>
            {
                while (!cts.IsCancellationRequested)
                {
                    await Task.Delay(500);

                    if (!IsStart) continue;

                    bool state = serialPort.IsOpen;

                    if (!state)
                    {
                        state = OpenSerial();
                    }

                    if (state)
                    {
                        this.ShowMessage("串口打开正常！");
                        try
                        {
                            foreach (var item in this.ItemList)
                            {
                                await this.Read(item);
                            }
                        }
                        catch (Exception ex)
                        {
                            LogProvider.WriteLog("读取寄存器失败！" + ex.Message, this.IsLog, isError: true);
                            this.ShowMessage("读取寄存器失败！" + ex.Message, false);
                        }
                    }
                }
            }, cts.Token);
            tasks.Add(task);
        }

        private bool OpenSerial()
        {
            try
            {
                serialPort.PortName = this.PortName;
                serialPort.BaudRate = this.BaudRate;

                serialPort.Open();
                LogProvider.WriteLog("串口打开成功", this.IsLog);
                return true;
            }
            catch (Exception ex)
            {
                LogProvider.WriteLog("串口打开失败！" + ex.Message, this.IsLog, isError: true);
                this.ShowMessage("串口打开失败！" + ex.Message, false);
                return false;
            }
        }

        private async Task Read(ItemModel model)
        {
            try
            {
                byte[] bytes = new byte[8];
                bytes[0] = (byte)model.SlaveId;
                bytes[1] = 0x03;
                bytes[2] = 0x00;
                bytes[3] = 0x00;
                bytes[4] = 0x00;
                bytes[5] = 0x02;

                // CRC校验
                CRC16(bytes);
                // 发送报文
                this.serialPort.Write(bytes, 0, bytes.Length);

                await Task.Delay(100);

                byte[] respBytes = new byte[serialPort.BytesToRead];
                // 获取报文
                this.serialPort.Read(respBytes, 0, respBytes.Length);

                byte[] checkArray = new byte[respBytes.Length];
                Array.Copy(respBytes, checkArray, respBytes.Length);
                this.CRC16(checkArray);

                if (!Enumerable.SequenceEqual(respBytes, checkArray))
                    throw new Exception("接收报文校验错误");

                if ((checkArray[1] & 0x80) == 0x80)
                    throw new Exception("异常码 - " + checkArray[2]);

                var temp = BitConverter.ToInt16(new byte[] { checkArray[4], checkArray[3] }, 0);
                var humi = BitConverter.ToInt16(new byte[] { checkArray[6], checkArray[5] }, 0);

                LogProvider.WriteLog($"从站[{model.SlaveId}]数据获取成功 - {temp * 0.1}/{humi * 0.1}", this.IsLog);

                model.Temperature = temp * 0.1;
                model.Humidity = humi * 0.1;
            }
            catch (Exception ex)
            {
                LogProvider.WriteLog($"从站[{model.SlaveId}]数据获取失败！" + ex.Message, this.IsLog);
                throw ex;
            }
        }

        private void CRC16(byte[] value)
        {
            if (value == null || !value.Any())
                throw new ArgumentException("");

            //运算
            ushort crc = 0xFFFF;
            for (int i = 0; i < value.Length - 2; i++)
            {
                crc = (ushort)(crc ^ (value[i]));
                for (int j = 0; j < 8; j++)
                {
                    crc = (crc & 1) != 0 ? (ushort)((crc >> 1) ^ 0xA001) : (ushort)(crc >> 1);
                }
            }
            byte hi = (byte)((crc & 0xFF00) >> 8);  //高位置
            byte lo = (byte)(crc & 0x00FF);         //低位置

            value[value.Length - 2] = lo;
            value[value.Length - 1] = hi;
        }

        private void ShowMessage(string message, bool state = true)
        {
            this.Message = message;
            this.MsgColor = state ? "#90EE90" : "#FF4500";
        }

        public void Dispose()
        {
            cts.Cancel();
            Task.WaitAll(tasks.ToArray(), 10000);

            serialPort.Close();

            LogProvider.WriteLog("对象已释放，已销毁", this.IsLog);
            LogProvider.Dispose();
        }
    }
}
